package com.gopay.checkorder.dao.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.gopay.checkorder.dao.BankOrderDAO;
import com.gopay.common.base.dao.impl.BaseDAOImpl;
import com.gopay.common.constants.bank.BankOrderLock;
import com.gopay.common.constants.bank.BankOrderStatus;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.domain.bank.BankOrder;
import com.gopay.common.domain.bank.CheckOrderFailLog;
import com.gopay.common.domain.bank.CheckOrderLog;
import com.gopay.common.util.DateUtils;

/**
 * 银行流水DAO实现
 * 
 * @BankOrderDAOImpl.java
 * @author yaobo @2013-3-19 下午02:36:38 www.gopay.com.cn Inc.All rights reserved.
 */
@Repository(value = "bankOrderDAO")
public class BankOrderDAOImpl extends BaseDAOImpl<BankOrder, String>implements BankOrderDAO {

    @Override
    public int lock(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set lock = :lock");
        hql.append(",lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");
        //add by zyt,2017-10-10,增加当前锁定条件的判断，
        hql.append(" and lock = :curLock");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("lock", BankOrderLock.LOCK);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());
        //add by zyt,2017-10-10,当前为未锁定的时候，才加锁
        params.put("curLock", BankOrderLock.UN_LOCK);

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public int unlock(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set lock = :lock");
        hql.append(",lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");
        //add by zyt,2017-10-10,增加当前锁定条件的判断，
        hql.append(" and lock = :curLock");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("lock", BankOrderLock.UN_LOCK);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());
        //add by zyt,2017-10-10,当前为锁定的时候，才解锁
        params.put("curLock", BankOrderLock.LOCK);

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public int updateTotalCheckTimes(BankOrder bankOrder) {
        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        hql.append("lastUpdTs = :lastUpdTs ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes() + 1);
        params.put("lastUpdTs", DateUtils.getSystemDate());
        params.put("outOrderId", bankOrder.getOutOrderId());

        int result = this.updateByHql(hql.toString(), params);

        return result;
    }

    @Override
    public int updateCancel(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set orderStatus = :orderStatus , ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        hql.append("lastCheckBillTime = to_date( :lastCheckBillTime ,'yyyy-mm-dd hh24:mi:ss'),");
        hql.append("lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", BankOrderStatus.CANCEL);
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes());
        params.put("lastCheckBillTime", currDate);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public int updateCheckFail(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set orderStatus = :orderStatus , ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        if (BisBankBusinessProd._12.value.equals(bankOrder.getBankBizProdCode())) {
            // 退款产品记录失败原因 add by zhengxue 20160909
            hql.append("remark2 = :remark2 ,");
        }
        hql.append("lastCheckBillTime = to_date( :lastCheckBillTime ,'yyyy-mm-dd hh24:mi:ss'),");
        hql.append("tranFinishTime = to_date( :tranFinishTime ,'yyyy-mm-dd hh24:mi:ss'),");
        hql.append("lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", BankOrderStatus.FAIL);
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes());
        if (BisBankBusinessProd._12.value.equals(bankOrder.getBankBizProdCode())) {
            // 退款产品记录失败原因 add by zhengxue 20160909
            params.put("remark2", bankOrder.getRemark2());
        }
        params.put("lastCheckBillTime", currDate);
        params.put("tranFinishTime", currDate);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public int updateEnterAccountFail(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set orderStatus = :orderStatus , ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        hql.append("lastCheckBillTime = to_date( :lastCheckBillTime ,'yyyy-mm-dd hh24:mi:ss'),");
        hql.append("lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", BankOrderStatus.ENTER_ACCOUNT_FIAL);
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes());
        params.put("lastCheckBillTime", currDate);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public int updateCheckSuccess(BankOrder bankOrder, String bankOrderStatus) {
        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set orderStatus = :orderStatus , ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        hql.append("lastCheckBillTime = :lastCheckBillTime ,");
        hql.append("tranFinishTime = :tranFinishTime,");
        hql.append("lastUpdTs = :lastUpdTs ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", bankOrderStatus);
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes());
        params.put("lastCheckBillTime", DateUtils.getSystemDate());
        params.put("tranFinishTime", DateUtils.getSystemDate());
        params.put("lastUpdTs", DateUtils.getSystemDate());
        params.put("outOrderId", bankOrder.getOutOrderId());

        int result = this.updateByHql(hql.toString(), params);

        return result;
    }

    @Override
    public BankOrder getByGopayOutOrderId(String gopayOutOrderId) {
        String hql = "from BankOrder where outOrderId = :outOrderId";

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("outOrderId", gopayOutOrderId);

        List<BankOrder> list = this.findByHql(hql, params);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }

        return null;
    }

    @Override
    public void insertCheckOrderLog(CheckOrderLog checkOrderLog) {

        this.getHibernateTemplate().save(checkOrderLog);
    }

    // 更新对账日志表，更新字段特殊，用hql处理
    @Override
    public int updateCheckOrderLog(CheckOrderLog checkOrderLog, String respcode, Date checktime, int totalCheckTime) {
        String hql = "update CheckOrderLog set isCheckOk= :isCheckOk,"
                + "respcode= :respcode,checktime= :checktime,checkCount= :checkCount where outOrderId= :outOrderId";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("outOrderId", checkOrderLog.getOutOrderId());
        params.put("isCheckOk", checkOrderLog.getIsCheckOk());
        params.put("respcode", respcode);
        params.put("checktime", checktime);
        params.put("checkCount", totalCheckTime);
        int result = this.updateByHql(hql.toString(), params);
        return result;
    }

    @Override
    public void insertCheckOrderFailLog(CheckOrderFailLog checkOrderFailLog) {
        this.getHibernateTemplate().save(checkOrderFailLog);

    }

    /**
     * 根据银行（bank_code）进行分组，查询日期（transTime）最新的记录，查询十分钟之内数据
     */
    public List getCheckOrderFailLog() {
        String sql = "SELECT bank_code FROM (SELECT bo.bank_code,MAX(bo.trans_time) FROM BIS_CHECK_ORDER_FAIL_LOG bo WHERE bo.trans_time>=(select sysdate - interval '10' minute from dual) GROUP BY bo.bank_code)";
        return this.findBySql(sql);
    }

    @Override
    public List<BankOrder> getCheckOrder(Date date, int pageIndex, int pageSize) {
        date = getNextDay(date);
        StringBuffer hql = new StringBuffer("");
        hql.append("from BankOrder ");
        hql.append("where orderStatus =:orderStatus ");
        hql.append(" and sendBankTm >= :date ");
        hql.append(" and bankBizProdCode != :bankBizProdCode and totalCheckTimes >= :totalCheckTimes ");

        hql.append("order by orderId");

        Map<String, Object> params = new HashMap<String, Object>();
        String status = BankOrderStatus.PROCESS;
        params.put("orderStatus", status);
        params.put("date", date);
        params.put("totalCheckTimes", 7);
        params.put("bankBizProdCode", BisBankBusinessProd._09.value);
        List<BankOrder> list = this.findByHql(hql.toString(), params, pageIndex, pageSize);

        return list;
    }

    @Override
    public int getCheckOrderCount(Date date) {
        date = getNextDay(date);
        StringBuffer hql = new StringBuffer("");
        hql.append("select count(*) from BankOrder ");
        hql.append("where orderStatus = :orderStatus ");
        hql.append(" and sendBankTm >= :date and totalCheckTimes >= :totalCheckTimes ");
        hql.append(" and bankBizProdCode != :bankBizProdCode ");
        Map<String, Object> params = new HashMap<String, Object>();
        String status = BankOrderStatus.PROCESS;
        params.put("orderStatus", status);
        params.put("totalCheckTimes", 7);
        params.put("date", date);
        params.put("bankBizProdCode", BisBankBusinessProd._09.value);
        int rsCount = this.findCountByHql(hql.toString(), params);

        return rsCount;
    }

    private Date getNextDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        date = calendar.getTime();
        return date;
    }

    @Override
    public List<BankOrder> getGopayOrders(String bank, String date) {
        String hql = "from BankOrder where outOrderId like :outOrderId and bankCode = :bankCode ";
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("outOrderId", date + "%");
        param.put("bankCode", bank);
        return this.findByHql(hql, param);
    }

    @Override
    public int updateAmountError(BankOrder bankOrder) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());

        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set orderStatus = :orderStatus , ");
        hql.append("totalCheckTimes = :totalCheckTimes ,");
        hql.append("lastCheckBillTime = to_date( :lastCheckBillTime ,'yyyy-mm-dd hh24:mi:ss'),");
        hql.append("lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", BankOrderStatus.AMOUNT_ERROR);
        params.put("totalCheckTimes", bankOrder.getTotalCheckTimes());
        params.put("lastCheckBillTime", currDate);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", bankOrder.getOutOrderId());

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public BankOrder getByGopayOrderId(String gopayOrderId) {
        String hql = "from BankOrder where orderId = :orderId";

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderId", gopayOrderId);

        List<BankOrder> list = this.findByHql(hql, params);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }

        return null;
    }

    @Override
    public int updateBankOrderId(String outOrderId, String payMsgId) {
        String currDate = DateUtils.format(DateUtils.getSystemDate());
        StringBuffer hql = new StringBuffer();
        hql.append("update BankOrder set bankOrderId = :bankOrderId , ");
        hql.append("lastUpdTs = to_date( :lastUpdTs ,'yyyy-mm-dd hh24:mi:ss') ");
        hql.append("where outOrderId = :outOrderId");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("bankOrderId", payMsgId);
        params.put("lastUpdTs", currDate);
        params.put("outOrderId", outOrderId);

        return this.updateByHql(hql.toString(), params);
    }

    @Override
    public List getCheckOrderFailLog(String failCount) {
        Map<String, Object> params = new HashMap<>();
        params.put("failCount", failCount);
        String sql = "select distinct t.bank_code" +
                "  from BIS_CHECK_ORDER_FAIL_LOG t" +
                "  where not exists (select 1" +
                "          from BIS_CHECK_ORDER_FAIL_LOG t2" +
                "         where t.bank_code = t2.bank_code and" +
                "            t.trans_time < t2.trans_time)" +
                "   and t.fail_count >= :failCount" +
                "   and t.trans_time >=" +
                "               (select sysdate - interval '10' minute from dual)";
        return this.findBySql(sql, params);
    }
}
